package neuron;

import java.util.ArrayList;
import java.util.List;

import common.statistics.Series;

/**
 * 
 * Compute statistics for axon or dendrite.
 * 
 * @author risto
 *
 */
public class Statistics {

	private List<Segment> segments;
	
	public Statistics(List<Segment> segments) {
		this.segments = segments;
	}

	public Statistics(Segment rootSegment)
	{
		segments = new ArrayList<Segment>();
		rootSegment.collectSegments(segments);
	}
	
	public static Statistics from(Segment rootSegment)
	{
		return new Statistics(rootSegment);
	}
	
	/**
	 * Count terminal segments.
	 * 
	 * @return
	 */
	public int countTerminalSegments() {
		int terminalCount = 0;
		for (Segment s : segments) {
			if (s.isTerminalSegment()) terminalCount++;
		}
		return terminalCount;
	}
	
	/**
	 * Count branches.
	 */
	public int countBranches() {
		int branchCount = 0;
		for (Segment s : segments) {
			if (!s.isTerminalSegment()) branchCount++;
		}
		return branchCount;
	}
	
	/**
	 * Terminal segment lengths
	 * @return
	 */
	public Series terminalSegmentLengths()
	{
		Series s = new Series();
		for (Segment seg : segments) {
			if (seg.isTerminalSegment()) s.add(seg.length());
		}
		return s;
	}
	
	/**
	 * Intermediate segment lengths
	 * @return
	 */
	public Series intermediateSegmentLengths()
	{
		Series s = new Series();
		for (Segment seg : segments) {
			if (seg.isIntermediateSegment()) s.add(seg.length());
		}
		return s;
	}
	
	/**
	 * All segment lengths
	 * @return
	 */
	public Series allSegmentLengths()
	{
		Series s = new Series();
		for (Segment seg : segments) {
			s.add(seg.length());
		}
		return s;
	}
	
	/**
	 * Asymmetric index A_t.
	 * 
	 * Note: Computation will ignore tri-furcations. 
	 */
	public double asymmetricIndex()
	{
		double at = 0;
		int n = 0;
		
		for (Segment seg : segments) {
			Segment left = seg.getBranch(0);
			Segment right = seg.getBranch(1);
			
			int r, s;

			if (left != null) { r = Statistics.from(left).countTerminalSegments(); } 
			else { r = 1; }
			
			if (right != null) { s = Statistics.from(right).countTerminalSegments(); } 
			else { s = 1; }

			// Partition asymmetry Ap(R,S) = |R-S| / (R+S-2). Exception Ap(1,1) = 0. 
			int diff = Math.abs(r-s);
			int denom = r+s-2;
            double ap = 1.0*diff/denom;
            if (denom == 0) ap = 0;

            // Asymmetry index At = Sum(Ap), for all Ap
            at += ap;
            n++;
		}
		return at/n;
	}
	
	/**
	 * Total length.
	 */
	public double totalLength()
	{
		return allSegmentLengths().sum();
	}
	
	/**
	 * Centrifugal order
	 */
	public Series centrifugalOrder()
	{
		Series s = new Series();
		centrifugalOrderRec(s, segments.get(0), 0);
		return s;
	}
	
	/**
	 * Recursive computation of centrifugal order
	 */
	private void centrifugalOrderRec(Series s, Segment seg, int order)
	{
		// add current segment to series
		s.add(order);
		// recurse into children (if any)
		for (Segment br : seg.getBranches()) {
			if (br != null) { centrifugalOrderRec(s, br, order+1); } 
		}
	}
	
}
